package cn.com.nes.common.util.iot;

import cn.com.nes.mybatis.agent.ope.entity.IsProtocolQueryCommand;
import cn.com.nes.mybatis.agent.ope.mapper.IsProtocolQueryCommandMapper;
import cn.com.nes.site.entity.em.RegisterTypeEnum;
import cn.com.nes.site.entity.iot.NesProtocolDataSet;
import cn.com.nes.site.module.service.iot.ProtocolService;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ModbusProtocolUtil {

    // 生成查询指令
    public void generateQueryCommand(List<NesProtocolDataSet> nesProtocolDataSets,
                                                              String protocolId, String username, String slaveAddress,
                                                              int maxReadRegisterCount1Bit, int maxReadRegisterCount16Bit,
                                                              ProtocolService protocolService,
                                                              IsProtocolQueryCommandMapper queryCommandMapper) {
        if (nesProtocolDataSets == null || nesProtocolDataSets.size() == 0) {
            return;
        }

        List<IsProtocolQueryCommand> queryCommandList = queryCommandMapper.getQueryCommand(protocolId, null);

        // add by zhouy(查询所有readable=false的数据项,把查询语句删掉)
        deleteQueryCommand(nesProtocolDataSets,queryCommandList,queryCommandMapper);

        // 未被查询指令覆盖的寄存器地址
        List<NesProtocolDataSet> uncovered = findUncoveredRegister(nesProtocolDataSets, queryCommandList);

        // 此处代码的作用？设置setStartByte，把字段的查询指令设置为空？
        for (NesProtocolDataSet dataSet : nesProtocolDataSets) {
            if (dataSet.getQueryCommand() != null) {
                String startAddressHEX = dataSet.getQueryCommand().getStartRegisterAddress();
                int startAddress = Integer.parseInt(startAddressHEX, 16);
                RegisterTypeEnum registerType = RegisterTypeEnum.valueOf(dataSet.getDataItemInfo().getRegisterType());
                int offset = (dataSet.getRegisterDataItem().getRegisterAddress() - startAddress)
                        * registerType.getBitLength();
                // 16bit寄存器截取bit位
                if (dataSet.getRegisterDataItem().getBitAddress() != null && registerType.getBitLength() > 1) {
                    // modify by zhouy
                    if(! dataSet.getRegisterDataItem().getBitAddress().contains("-")) {
                        int bitAddress = Integer.parseInt(dataSet.getRegisterDataItem().getBitAddress());
                        offset += (bitAddress + 8) % 16;
                    }
                    // bit位顺序 8-15 0-7
                }
                dataSet.getDataItemInfo().setStartByte(offset);
            }
            dataSet.setQueryCommand(null);
        }

        // 都有查询指令，不用再处理
        if (uncovered == null) {
            return;
        }

        //uncovered中的字段,生成查询指令
        for (RegisterTypeEnum registerTypeEnum : RegisterTypeEnum.values()) {
            QueryCommandBuffer queryCommandBuffer = new QueryCommandBuffer(protocolId, username, slaveAddress,
                    protocolService, maxReadRegisterCount1Bit, maxReadRegisterCount16Bit);
            for (NesProtocolDataSet dataSet : uncovered) {
                if (dataSet.getDataItemInfo().getRegisterType().equals(registerTypeEnum.name())) {
                    queryCommandBuffer.append(dataSet);
                }
            }
            queryCommandBuffer.flush();
        }
    }

    /**
     * 删除readable=false的查询指令
     * @param nesProtocolDataSets
     * @param queryCommandList
     */
    public void deleteQueryCommand(List<NesProtocolDataSet> nesProtocolDataSets,List<IsProtocolQueryCommand> queryCommandList,IsProtocolQueryCommandMapper queryCommandMapper){

        // 查找出readable =false的数据项
        List<NesProtocolDataSet> temp = new ArrayList<>();
        for (NesProtocolDataSet nesData : nesProtocolDataSets) {
            if(!nesData.getRegisterDataItem().getReadable()){
                temp.add(nesData);
            }
        }

        if(temp.size()==0) {
            return;
        }

        for(NesProtocolDataSet nesData : temp) {
            final int address = nesData.getDataItemInfo().getRegisterAddress();
            final int length = nesData.getDataItemInfo().getByteLength();
            for (IsProtocolQueryCommand queryCommand : queryCommandList) {
                final ModbusQueryCommand modbusQueryCommand = new ModbusQueryCommand(queryCommand.getCommand(), queryCommand.getQueryCommandId());
                if (modbusQueryCommand.contains(address, length)) {
                    queryCommandMapper.deleteById(queryCommand.getId());
                    queryCommandList.remove(queryCommand);
                    // 删除查询
                    break;
                }
            }
        }
    }


    public void deleteQueryCommand(NesProtocolDataSet nesProtocolDataSet,List<IsProtocolQueryCommand> queryCommandList,IsProtocolQueryCommandMapper queryCommandMapper){

        final int address = nesProtocolDataSet.getDataItemInfo().getRegisterAddress();
        final int length = nesProtocolDataSet.getDataItemInfo().getByteLength();
        for (IsProtocolQueryCommand queryCommand : queryCommandList) {
            final ModbusQueryCommand modbusQueryCommand = new ModbusQueryCommand(queryCommand.getCommand(), queryCommand.getQueryCommandId());
            if (modbusQueryCommand.contains(address, length)) {
                queryCommandMapper.deleteById(queryCommand.getId());
                queryCommandList.remove(queryCommand);
                // 删除查询
                break;
            }
        }

    }


    // 查询是否有未被查询指令覆盖的寄存器
    private List<NesProtocolDataSet> findUncoveredRegister(List<NesProtocolDataSet> nesProtocolDataSets,
                                                           List<IsProtocolQueryCommand> queryCommandList) {
        if (nesProtocolDataSets == null || nesProtocolDataSets.size() == 0) {
            return null;
        }

        // 未被查询指令覆盖的寄存器地址
        List<NesProtocolDataSet> uncoveredRegisterAddresses = new ArrayList<>();

        Map<String, List<ModbusQueryCommand>> queryCommandMap = new HashMap<>();
        for (IsProtocolQueryCommand queryCommand : queryCommandList) {
            final ModbusQueryCommand modbusQueryCommand = new ModbusQueryCommand(queryCommand.getCommand(),
                    queryCommand.getQueryCommandId());
            final String registerType = modbusQueryCommand.registerType;
            if (!queryCommandMap.containsKey(registerType)) {
                queryCommandMap.put(registerType, new ArrayList<>());
            }
            queryCommandMap.get(registerType).add(modbusQueryCommand);
        }

        for (NesProtocolDataSet nesData : nesProtocolDataSets) {
            final String registerType = nesData.getDataItemInfo().getRegisterType();
            final int address = nesData.getDataItemInfo().getRegisterAddress();
            final int length = nesData.getDataItemInfo().getByteLength();
            final List<ModbusQueryCommand> modbusQueryCommands = queryCommandMap.get(registerType);
            if (modbusQueryCommands == null || modbusQueryCommands.size() == 0) {
                uncoveredRegisterAddresses.add(nesData);
                continue;
            }
            boolean isCovered = false;
            for (ModbusQueryCommand modbusQueryCommand : modbusQueryCommands) {
                if (modbusQueryCommand.contains(address, length)) {
                    nesData.getDataItemInfo().setQueryCommandId(modbusQueryCommand.commandId);
                    isCovered = true;
                    nesData.setQueryCommand(new IsProtocolQueryCommand());
                    nesData.getQueryCommand().setStartRegisterAddress(modbusQueryCommand.startAddress);
                    break;
                }
            }
            if (!isCovered) {
                uncoveredRegisterAddresses.add(nesData);
            }
        }
        return uncoveredRegisterAddresses;
    }

    private static class ModbusQueryCommand {

        String commandId;

        String command;

        String slaveAddress;

        String functionCode;

        String startAddress;

        String registerCount;

        String crc;

        String registerType;

        int startAddressInt;

        int registerCountInt;

        int endAddressInt;

        ModbusQueryCommand(String command, String commandId) {
            this.commandId = commandId;
            this.command = command;
            this.slaveAddress = command.substring(0, 2);
            this.functionCode = command.substring(2, 4);
            this.startAddress = command.substring(4, 8);
            this.registerCount = command.substring(8, 12);
            this.crc = command.substring(12, 16);
            this.startAddressInt = Integer.parseInt(this.startAddress, 16);
            this.registerCountInt = Integer.parseInt(this.registerCount, 16);
            this.endAddressInt = this.startAddressInt + this.registerCountInt - 1;
            switch (this.functionCode) {
                case "01":
                    this.registerType = RegisterTypeEnum.DO.name();
                    break;
                case "02":
                    this.registerType = RegisterTypeEnum.DI.name();
                    break;
                case "03":
                    this.registerType = RegisterTypeEnum.HR.name();
                    break;
                case "04":
                    this.registerType = RegisterTypeEnum.AR.name();
                    break;
            }
        }

        public boolean contains(int address, int length) {
            length = length / 16 == 0 ? 1 : length / 16;
            int endAddress = address + length - 1;
            return address >= this.startAddressInt && endAddress <= this.endAddressInt;
        }

    }

}
